fix: surface registry fetch errors in GET /templates/registries (#2342)#2355
Merged
kmendell merged 3 commits intogetarcaneapp:mainfrom Apr 12, 2026
Merged
Conversation
…rcaneapp#2342) When a template registry fails to load (e.g. due to SSRF protection introduced in v1.17.3 blocking the registry URL, or a transient network error), the error was silently swallowed and users had no way to diagnose why their templates stopped appearing after upgrading. Track the last fetch error per registry ID in a lightweight in-memory map on TemplateService. On success the entry is cleared; on failure the error string is stored. The existing GET /templates/registries handler now overlays this info as lastFetchError on each registry response object, making the root cause visible in the UI/API without touching the database schema.
Member
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Comment on lines
+557
to
+561
| mu.Lock() | ||
| defer mu.Unlock() | ||
| s.registryMu.Lock() | ||
| delete(s.registryErrors, reg.ID) | ||
| s.registryMu.Unlock() |
Contributor
There was a problem hiding this comment.
Unnecessary lock nesting in success path
s.registryMu is acquired while mu is already held. The registry-error cleanup and the templates-slice append are independent operations; holding mu across the registryMu lock/unlock needlessly extends the window during which mu is held and couples two unrelated critical sections.
Suggested change
| mu.Lock() | |
| defer mu.Unlock() | |
| s.registryMu.Lock() | |
| delete(s.registryErrors, reg.ID) | |
| s.registryMu.Unlock() | |
| s.registryMu.Lock() | |
| delete(s.registryErrors, reg.ID) | |
| s.registryMu.Unlock() | |
| mu.Lock() | |
| defer mu.Unlock() |
Rule Used: # Go Development Patterns
What: Code should p... (source)
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/internal/services/template_service.go
Line: 557-561
Comment:
**Unnecessary lock nesting in success path**
`s.registryMu` is acquired while `mu` is already held. The registry-error cleanup and the templates-slice append are independent operations; holding `mu` across the `registryMu` lock/unlock needlessly extends the window during which `mu` is held and couples two unrelated critical sections.
```suggestion
s.registryMu.Lock()
delete(s.registryErrors, reg.ID)
s.registryMu.Unlock()
mu.Lock()
defer mu.Unlock()
```
**Rule Used:** # Go Development Patterns
**What:** Code should p... ([source](https://app.greptile.com/review/custom-context?memory=c1082b6a-5fdc-4db8-8419-8a71ccd57636))
How can I resolve this? If you propose a fix, please make it concise.
kmendell
approved these changes
Apr 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
loadRemoteTemplates(return nil // Don't fail the whole group), leaving users with no way to diagnose why templates stopped loading after upgrading.ValidateSafeRemoteURL) performs a DNS lookup on the registry URL. In restricted network environments (no outbound DNS, private registries on RFC1918 addresses) this blocks the request with no user-visible feedback.TemplateService. On success the entry is cleared. The existingGET /templates/registriesresponse now includeslastFetchErroron each registry object so users can see the exact error without checking server logs.Closes #2342
Test plan
GET /templates/registries— verifylastFetchErroris populated with the error messagelastFetchErrordisappears from the next response after a successful fetch🤖 Generated with Claude Code
Disclaimer Greptiles Reviews use AI, make sure to check over its work.
To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike
To have Greptile Re-Review the changes, mention
greptileai.Greptile Summary
This PR surfaces per-registry fetch errors (previously silently swallowed) through a new in-memory
registryErrorsmap onTemplateService, exposed viaGetRegistryFetchErrors()and overlaid onto theGET /templates/registriesresponse aslastFetchError. The approach is lightweight, correctly protected by the existingregistryMuRWMutex, and non-breaking. Two minor housekeeping gaps: (1) the success path holds the localmuwhile acquiringregistryMu, unnecessarily coupling two independent critical sections; (2)DeleteRegistrydoes not remove the registry's entry fromregistryErrors, leaving a small orphaned entry in the map.Confidence Score: 5/5
Safe to merge; all findings are minor style/cleanup items that don't affect correctness.
The core logic is correct — the mutex usage is sound (no deadlock risk), the snapshot copy in GetRegistryFetchErrors is proper, and the DTO change is backward-compatible. Both flagged issues are P2 quality improvements rather than correctness or reliability bugs.
backend/internal/services/template_service.go — lock nesting and missing DeleteRegistry cleanup
Comments Outside Diff (1)
backend/internal/services/template_service.go, line 509-526 (link)registryErrorsentry not removed on registry deletionWhen a registry is deleted, its entry in
s.registryErrorsis never cleaned up. With UUID IDs the practical impact is negligible, but it is a minor memory leak and would surface stale error state if the ID were ever reused. Clearing the entry here alongsideinvalidateRemoteCache()keeps the map consistent with the database.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "fix: surface registry fetch errors in GE..." | Re-trigger Greptile
Context used:
What: Code should p... (source)